package com.edu.learning.concurrent.custom.queue;

import java.util.LinkedList;

/**
 * 使用 Object.wait()/notifyAll() 实现阻塞队列
 *
 * @Author ll
 * @Date 2017/10/16 11:15
 *
 * 注意要点
 * 1,判定 LinkedList大小为0或者大于等于queueSize时须使用 while (condition) {}，不能使用 if(condition) {}。
 * 其中 while(condition) 循环，它又被叫做“自旋锁”。
 * 为防止该线程没有收到 notify() 调用也从 wait() 中返回（也称作虚假唤醒），
 * 这个线程会重新去检查 condition 条件以决定当前是否可以安全地继续执行还是需要重新保持等待，而不是认为线程被唤醒了就可以安全地继续执行了。
 *
 * 2,在 take 方法取走一个元素后须调用 lock.notifyAll();
 * 如果使用 lock.notify(); 方法在某些情况下会导致 生产者-消费者 同时处于阻塞状态。
 */
public class TraditionalBlockingQueue<T> implements IBlockingQueue<T> {

    private int queueSize;
    private final LinkedList<T> list = new LinkedList<T>();
    private final Object lock = new Object();

    public TraditionalBlockingQueue() {
        this(10);
    }

    public TraditionalBlockingQueue(int queueSize ) {
        if (queueSize < 1) {
            throw new IllegalArgumentException("queueSize must be meaningful number");
        }
        this.queueSize = queueSize;
    }

    @Override
    public void put(T data) throws InterruptedException {
        synchronized (lock) {
            while (list.size() >= queueSize) {
                lock.wait();
            }
            list.addLast(data);
            lock.notify();
        }
    }

    @Override
    public T take() throws InterruptedException {
        synchronized (lock) {
            while (list.size() <= 0) {
                lock.wait();
            }
            T data = list.removeFirst();
            lock.notifyAll();
            return data;
        }
    }
}
